package com.ylsk.framework.config.ext;

import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.resultset.ResultSetWrapper;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.NoSuchElementException;

public class CustomDefaultCursor<T> implements Cursor<T>  {
    private final CustomDefaultResultSetHandler resultSetHandler;
    private final ResultMap resultMap;
    private final ResultSetWrapper rsw;
    private final RowBounds rowBounds;
    protected final CustomDefaultCursor.ObjectWrapperResultHandler<T> objectWrapperResultHandler = new CustomDefaultCursor.ObjectWrapperResultHandler();
    private final CustomDefaultCursor<T>.CursorIterator cursorIterator = new CustomDefaultCursor.CursorIterator();
    private boolean iteratorRetrieved;
    private CustomDefaultCursor.CursorStatus status;
    private int indexWithRowBound;

    public CustomDefaultCursor(CustomDefaultResultSetHandler resultSetHandler, ResultMap resultMap, ResultSetWrapper rsw, RowBounds rowBounds) {
        this.status = CustomDefaultCursor.CursorStatus.CREATED;
        this.indexWithRowBound = -1;
        this.resultSetHandler = resultSetHandler;
        this.resultMap = resultMap;
        this.rsw = rsw;
        this.rowBounds = rowBounds;
    }

    public boolean isOpen() {
        return this.status == CustomDefaultCursor.CursorStatus.OPEN;
    }

    public boolean isConsumed() {
        return this.status == CustomDefaultCursor.CursorStatus.CONSUMED;
    }

    public int getCurrentIndex() {
        return this.rowBounds.getOffset() + this.cursorIterator.iteratorIndex;
    }

    public Iterator<T> iterator() {
        if (this.iteratorRetrieved) {
            throw new IllegalStateException("Cannot open more than one iterator on a Cursor");
        } else if (this.isClosed()) {
            throw new IllegalStateException("A Cursor is already closed.");
        } else {
            this.iteratorRetrieved = true;
            return this.cursorIterator;
        }
    }

    public void close() {
        if (!this.isClosed()) {
            ResultSet rs = this.rsw.getResultSet();

            try {
                if (rs != null) {
                    rs.close();
                }
            } catch (SQLException var6) {
            } finally {
                this.status = CustomDefaultCursor.CursorStatus.CLOSED;
            }

        }
    }

    protected T fetchNextUsingRowBound() {
        T result;
        for(result = this.fetchNextObjectFromDatabase(); this.objectWrapperResultHandler.fetched && this.indexWithRowBound < this.rowBounds.getOffset(); result = this.fetchNextObjectFromDatabase()) {
        }

        return result;
    }

    protected T fetchNextObjectFromDatabase() {
        if (this.isClosed()) {
            return null;
        } else {
            try {
                this.objectWrapperResultHandler.fetched = false;
                this.status = CustomDefaultCursor.CursorStatus.OPEN;
                if (!this.rsw.getResultSet().isClosed()) {
                    this.resultSetHandler.handleRowValues(this.rsw, this.resultMap, this.objectWrapperResultHandler, RowBounds.DEFAULT, (ResultMapping)null);
                }
            } catch (SQLException var2) {
                throw new RuntimeException(var2);
            }

            T next = this.objectWrapperResultHandler.result;
            if (this.objectWrapperResultHandler.fetched) {
                ++this.indexWithRowBound;
            }

            if (!this.objectWrapperResultHandler.fetched || this.getReadItemsCount() == this.rowBounds.getOffset() + this.rowBounds.getLimit()) {
                this.close();
                this.status = CustomDefaultCursor.CursorStatus.CONSUMED;
            }

            this.objectWrapperResultHandler.result = null;
            return next;
        }
    }

    private boolean isClosed() {
        return this.status == CustomDefaultCursor.CursorStatus.CLOSED || this.status == CustomDefaultCursor.CursorStatus.CONSUMED;
    }

    private int getReadItemsCount() {
        return this.indexWithRowBound + 1;
    }

    protected class CursorIterator implements Iterator<T> {
        T object;
        int iteratorIndex = -1;

        protected CursorIterator() {
        }

        public boolean hasNext() {
            if (!CustomDefaultCursor.this.objectWrapperResultHandler.fetched) {
                this.object = CustomDefaultCursor.this.fetchNextUsingRowBound();
            }

            return CustomDefaultCursor.this.objectWrapperResultHandler.fetched;
        }

        public T next() {
            T next = this.object;
            if (!CustomDefaultCursor.this.objectWrapperResultHandler.fetched) {
                next = CustomDefaultCursor.this.fetchNextUsingRowBound();
            }

            if (CustomDefaultCursor.this.objectWrapperResultHandler.fetched) {
                CustomDefaultCursor.this.objectWrapperResultHandler.fetched = false;
                this.object = null;
                ++this.iteratorIndex;
                return next;
            } else {
                throw new NoSuchElementException();
            }
        }

        public void remove() {
            throw new UnsupportedOperationException("Cannot remove element from Cursor");
        }
    }

    protected static class ObjectWrapperResultHandler<T> implements ResultHandler<T> {
        protected T result;
        protected boolean fetched;

        protected ObjectWrapperResultHandler() {
        }

        public void handleResult(ResultContext<? extends T> context) {
            this.result = context.getResultObject();
            context.stop();
            this.fetched = true;
        }
    }

    private static enum CursorStatus {
        CREATED,
        OPEN,
        CLOSED,
        CONSUMED;

        private CursorStatus() {
        }
    }

}
